package com.tiny.conf.core.core;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

public class TinyConfLocalCacheConf {
    private static Logger logger = LoggerFactory.getLogger(TinyConfLocalCacheConf.class);


    private static ConcurrentHashMap<String, CacheNode> localCacheRepository = null;

    // 定义一个线程
    private static Thread refreshThread;
    private static boolean refreshThreadStop = false;

    public static void init() {

        // 使用ConcurrentHashMap 保证线程安全
        localCacheRepository = new ConcurrentHashMap<String, CacheNode>();

        //该Java函数的功能是创建并启动一个刷新线程 refreshThread，用于定期执行配置的刷新操作。主要功能描述如下：
        //刷新线程循环：线程进入一个无限循环，直到 refreshThreadStop 标志被设置为 true。
        //定时暂停与刷新操作：在循环中，线程会每60秒调用一次 reloadAll() 方法来重新加载所有配置，并记录日志表明操作成功。
        //异常处理：如果在执行 reloadAll() 或检查停止标志时发生异常，且 refreshThreadStop 未设置为 true，则记录错误日志并继续尝试刷新。
        //停止日志记录：当循环因 refreshThreadStop 被设置为 true 而终止时，记录日志表明刷新线程已停止。
        //这个函数的主要目的是确保配置的实时性和有效性，通过定期刷新来响应可能的配置更改。
        refreshThread = new Thread(new Runnable() {

            @Override
            public void run() {
                while (!refreshThreadStop) {
                    try {
                        TimeUnit.SECONDS.sleep(60);
                        reloadAll();
                        logger.info(">>>>>>>>>> xxl-conf, refresh thread reload all success.");
                    } catch (Exception e) {
                        if (!refreshThreadStop) {
                            logger.error(">>>>>>>>>> xxl-conf, refresh thread error.");
                            logger.error(e.getMessage(), e);
                        }
                    }
                }
                logger.info(">>>>>>>>>> xxl-conf, refresh thread stoped.");
            }
        });

        //将线程设置为守护线程。这意味着该线程会在所有非守护线程（用户线程）结束后自动终止。
        // 守护线程通常用于提供后台服务，如垃圾回收和对象池。
        refreshThread.setDaemon(true);
        refreshThread.start();// 启动线程

        logger.info(">>>>>>>>>> xxl-conf, XxlConfLocalCacheConf init success.");
    }

    public static void destroy() {
        if (refreshThread != null) {
            refreshThreadStop = true;
            refreshThread.interrupt();
        }
    }


    /**
     * local cache node
     * 该类定义了一个名为CacheNode的静态内部类，用于表示缓存节点，实现了Serializable接口，确保对象可序列化。
     * 类包含一个字符串字段value，用于存储缓存值，并提供了构造方法和getter、setter方法来操作value。
     */
    public static class CacheNode implements Serializable {
        private static final long serialVersionUID = 42L;

        private String value;

        public CacheNode() {
        }

        public CacheNode(String value) {
            this.value = value;
        }

        public String getValue() {
            return value;
        }

        public void setValue(String value) {
            this.value = value;
        }
    }


    // ---------------------- util ----------------------

    /**
     * reload all conf (watch + refresh)
     * 该函数的功能是重新加载所有配置（监视并刷新）：
     * 1、获取本地缓存中的所有键。
     * 2、如果缓存中存在数据，则遍历每个键：
     *      从ZooKeeper获取对应键的配置数据。
     *      检查本地缓存中是否存在相同键的配置，如果存在且内容与ZooKeeper中的相同，则记录日志并跳过。
     *      否则，使用新数据更新本地缓存，并记录更新操作为"RELOAD"。
     * 注意：该函数依赖于XxlConfZkConf类的get方法和localCacheRepository对象的keySet、get、set方法。
     * 这些方法分别用于从ZooKeeper获取配置、获取本地缓存的键集合、根据键获取缓存值、设置缓存值
     */
    public static void reloadAll() {
        Set<String> keySet = localCacheRepository.keySet(); // 拿到所有的key

        if (keySet.size() > 0){
            //
            for (String key : keySet) {
                // 从zk 拿到对应的
                String zkData = TinyConfZkConf.get(key);// zk 的数据

                CacheNode existNode = localCacheRepository.get(key); // 拿到缓存的节点

                if (existNode !=null
                        && existNode.getValue() != null
                        && existNode.getValue().equals(zkData)){
                    // 说明值没有变化
                    logger.info(">>>>>>>>>> xxl-conf, key:{} not changed.", key);
                }else{
                    // 更新缓存,并通知
                    set(key, zkData, "RELOAD");
                }
            }
        }
    }

    /**
     * set conf (invoke listener)
     *该函数功能如下：
     *      将键值对（key, value）存入本地缓存。
     *      记录操作日志，显示配置项变更信息。
     *      调用监听器工厂的方法，通知所有注册的监听器配置项已发生变化。
     * @param key
     * @param value
     * @return
     */
    public static void set(String key, String value, String optType) {
        localCacheRepository.put(key, new CacheNode(value));
        logger.info(">>>>>>>>>> xxl-conf: {}: [{}={}]", optType, key, value);

//        XxlConfListenerFactory.onChange(key, value);
    }

    /**
     * update conf  (only update exists key)  (invoke listener)
     *
     * @param key
     * @param value
     */
    public static void update(String key, String value) {
        if (localCacheRepository.containsKey(key)) {
            set(key, value, "UPDATE");
        }
    }

    /**
     * remove conf
     *
     * @param key
     * @return
     */
    public static void remove(String key) {
        if (localCacheRepository.containsKey(key)) {
            localCacheRepository.remove(key);
        }
        logger.info(">>>>>>>>>> xxl-conf: REMOVE: [{}]", key);
    }

    /**
     * get conf
     *该函数名为get，功能是从本地缓存中获取指定键key对应的CacheNode对象。具体功能如下：
     * 如果缓存中存在该key，则返回对应的CacheNode对象；
     * 如果缓存中不存在该key，则返回null。
     * @param key
     * @return
     */
    public static CacheNode get(String key) {
        if (localCacheRepository.containsKey(key)) {
            CacheNode cacheNode = localCacheRepository.get(key);
            return cacheNode;
        }
        return null;
    }


}
